[HVM] Decouple the RTC from the PIT periodic timer
authorTim Deegan <Tim.Deegan@xensource.com>
Thu, 16 Nov 2006 17:07:23 +0000 (17:07 +0000)
committerTim Deegan <Tim.Deegan@xensource.com>
Thu, 16 Nov 2006 17:07:23 +0000 (17:07 +0000)
Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
xen/arch/x86/hvm/hvm.c
xen/arch/x86/hvm/i8259.c
xen/arch/x86/hvm/rtc.c
xen/arch/x86/hvm/svm/svm.c
xen/arch/x86/hvm/vioapic.c
xen/arch/x86/hvm/vmx/vmcs.c
xen/arch/x86/hvm/vmx/vmx.c
xen/include/asm-x86/hvm/hvm.h
xen/include/asm-x86/hvm/vmx/vmx.h
xen/include/asm-x86/hvm/vpt.h

index 85775d5ae1edea18c49383039398241c10adf109..a97e3e38382be6a5f5fbb70ee367dd274641918b 100644 (file)
@@ -74,6 +74,20 @@ void hvm_set_guest_time(struct vcpu *v, u64 gtime)
     hvm_funcs.set_tsc_offset(v, v->arch.hvm_vcpu.cache_tsc_offset);
 }
 
+void hvm_migrate_timers(struct vcpu *v)
+{
+    struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
+    struct PMTState *vpmt = &v->domain->arch.hvm_domain.pl_time.vpmt;
+
+    if ( pt->enabled )
+    {
+        migrate_timer(&pt->timer, v->processor);
+    }
+    migrate_timer(&vcpu_vlapic(v)->vlapic_timer, v->processor);
+    migrate_timer(&vpmt->timer, v->processor);
+    rtc_migrate_timers(v);
+}
+
 void hvm_do_resume(struct vcpu *v)
 {
     ioreq_t *p;
@@ -92,6 +106,9 @@ void hvm_do_resume(struct vcpu *v)
         pickup_deactive_ticks(pt);
     }
 
+    /* Re-enable the RTC timer if needed */
+    rtc_thaw(v);
+
     /* NB. Optimised for common case (p->state == STATE_IOREQ_NONE). */
     p = &get_vio(v->domain, v->vcpu_id)->vp_ioreq;
     while ( p->state != STATE_IOREQ_NONE )
index de61f96c8d6533b0ef8b23dbbae9f5874406fa72..c1710cdbae838c6e38bf291b7226cfbde28685d1 100644 (file)
@@ -536,8 +536,6 @@ int is_periodic_irq(struct vcpu *v, int irq, int type)
     int vec;
     struct periodic_time *pt =
         &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
-    struct RTCState *vrtc =
-        &(v->domain->arch.hvm_domain.pl_time.vrtc);
 
     if (pt->irq == 0) { /* Is it pit irq? */
         if (type == APIC_DM_EXTINT)
@@ -549,16 +547,6 @@ int is_periodic_irq(struct vcpu *v, int irq, int type)
             return 1;
     }
 
-    if (pt->irq == 8) { /* Or rtc irq? */
-        if (type == APIC_DM_EXTINT)
-            vec = domain_vpic(v->domain)->pics[1].irq_base;
-        else
-            vec = domain_vioapic(v->domain)->redirtbl[8].fields.vector;
-
-        if (irq == vec)
-            return is_rtc_periodic_irq(vrtc);
-    }
-
     return 0;
 }
 
index a2ae171d85b247babc6528477c08f93a01b44e6f..b669d60dfee1ceace7b56f1c0cce5efe1fcca440 100644 (file)
 
 /* #define DEBUG_RTC */
 
-void rtc_periodic_cb(struct vcpu *v, void *opaque)
+/* Callback that fires the RTC's periodic interrupt */
+void rtc_pie_callback(void *opaque)
 {
     RTCState *s = opaque;
-    s->cmos_data[RTC_REG_C] |= 0xc0;
-}
-
-int is_rtc_periodic_irq(void *opaque)
-{
-    RTCState *s = opaque;
-    return !(s->cmos_data[RTC_REG_C] & RTC_AF || 
-           s->cmos_data[RTC_REG_C] & RTC_UF);
+    struct hvm_domain *plat = &s->vcpu->domain->arch.hvm_domain;
+    struct vpic       *pic  = &plat->vpic;
+    /* Record that we have fired */
+    s->cmos_data[RTC_REG_C] |= (RTC_IRQF|RTC_PF); /* 0xc0 */
+    /* Fire */
+    pic_set_irq(pic, s->irq, 1);
+    /* Remember to fire again */
+    s->next_pie = NOW() + s->period;
+    set_timer(&s->pie_timer, s->next_pie);
 }
 
-static void rtc_timer_update(RTCState *s, int64_t current_time)
+/* Enable/configure/disable the periodic timer based on the RTC_PIE and
+ * RTC_RATE_SELECT settings */
+static void rtc_timer_update(RTCState *s)
 {
     int period_code; 
     int period;
 
-    period_code = s->cmos_data[RTC_REG_A] & 0x0f;
+    period_code = s->cmos_data[RTC_REG_A] & RTC_RATE_SELECT;
     if (period_code != 0 && (s->cmos_data[RTC_REG_B] & RTC_PIE)) {
         if (period_code <= 2)
             period_code += 7;
         
         period = 1 << (period_code - 1); /* period in 32 Khz cycles */
         period = DIV_ROUND((period * 1000000000ULL), 32768); /* period in ns */
-
+        s->period = period;
 #ifdef DEBUG_RTC
         printk("HVM_RTC: period = %uns\n", period);
 #endif
-
-        s->pt = create_periodic_time(period, RTC_IRQ, 0, rtc_periodic_cb, s);
-    } else if (s->pt) {
-        destroy_periodic_time(s->pt);
-        s->pt = NULL;
+        s->next_pie = NOW() + s->period;
+        set_timer(&s->pie_timer, s->next_pie);
+    } else {
+        stop_timer(&s->pie_timer);
     }
 }
 
@@ -105,7 +108,7 @@ static int rtc_ioport_write(void *opaque, uint32_t addr, uint32_t data)
             /* UIP bit is read only */
             s->cmos_data[RTC_REG_A] = (data & ~RTC_UIP) |
                 (s->cmos_data[RTC_REG_A] & RTC_UIP);
-            rtc_timer_update(s, hvm_get_clock(s->vcpu));
+            rtc_timer_update(s);
             break;
         case RTC_REG_B:
             if (data & RTC_SET) {
@@ -119,14 +122,14 @@ static int rtc_ioport_write(void *opaque, uint32_t addr, uint32_t data)
                 }
             }
             s->cmos_data[RTC_REG_B] = data;
-            rtc_timer_update(s, hvm_get_clock(s->vcpu));
+            rtc_timer_update(s);
             break;
         case RTC_REG_C:
         case RTC_REG_D:
             /* cannot write to them */
             break;
-        return 1;
         }
+        return 1;
     }
     return 0;
 }
@@ -172,7 +175,7 @@ static void rtc_copy_date(RTCState *s)
 
     s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
     s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
-    if (s->cmos_data[RTC_REG_B] & 0x02) {
+    if (s->cmos_data[RTC_REG_B] & RTC_24H) {
         /* 24 hour format */
         s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
     } else {
@@ -245,7 +248,7 @@ static void rtc_update_second(void *opaque)
     RTCState *s = opaque;
 
     /* if the oscillator is not in normal operation, we do not update */
-    if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
+    if ((s->cmos_data[RTC_REG_A] & RTC_DIV_CTL) != RTC_REF_CLCK_32KHZ) {
         s->next_second_time += 1000000000ULL;
         set_timer(&s->second_timer, s->next_second_time);
     } else {
@@ -361,22 +364,48 @@ static int handle_rtc_io(ioreq_t *p)
     return 0;
 }
 
+/* Stop the periodic interrupts from this RTC */
+void rtc_freeze(struct vcpu *v)
+{
+    RTCState *s = &v->domain->arch.hvm_domain.pl_time.vrtc;
+    stop_timer(&s->pie_timer);
+}
+
+/* Start them again */
+void rtc_thaw(struct vcpu *v)
+{
+    RTCState *s = &v->domain->arch.hvm_domain.pl_time.vrtc;
+    if ( (s->cmos_data[RTC_REG_A] & RTC_RATE_SELECT) /* Period is not zero */
+         && (s->cmos_data[RTC_REG_B] & RTC_PIE) ) 
+        set_timer(&s->pie_timer, s->next_pie);
+}
+
+/* Move the RTC timers on to this vcpu's current cpu */
+void rtc_migrate_timers(struct vcpu *v)
+{
+    RTCState *s = &v->domain->arch.hvm_domain.pl_time.vrtc;
+    migrate_timer(&s->second_timer, v->processor);
+    migrate_timer(&s->second_timer2, v->processor);
+    migrate_timer(&s->pie_timer, v->processor);
+}
+
 void rtc_init(struct vcpu *v, int base, int irq)
 {
     RTCState *s = &v->domain->arch.hvm_domain.pl_time.vrtc;
 
     s->vcpu = v;
     s->irq = irq;
-    s->cmos_data[RTC_REG_A] = 0x26;
-    s->cmos_data[RTC_REG_B] = 0x02;
-    s->cmos_data[RTC_REG_C] = 0x00;
-    s->cmos_data[RTC_REG_D] = 0x80;
+    s->cmos_data[RTC_REG_A] = RTC_REF_CLCK_32KHZ | 6; /* ~1kHz */
+    s->cmos_data[RTC_REG_B] = RTC_24H;
+    s->cmos_data[RTC_REG_C] = 0;
+    s->cmos_data[RTC_REG_D] = RTC_VRT;
 
     s->current_tm = gmtime(get_localtime(v->domain));
     rtc_copy_date(s);
 
     init_timer(&s->second_timer, rtc_update_second, s, v->processor);
     init_timer(&s->second_timer2, rtc_update_second2, s, v->processor);
+    init_timer(&s->pie_timer, rtc_pie_callback, s, v->processor);
 
     s->next_second_time = NOW() + 1000000000ULL;
     set_timer(&s->second_timer2, s->next_second_time);
@@ -390,4 +419,5 @@ void rtc_deinit(struct domain *d)
 
     kill_timer(&s->second_timer);
     kill_timer(&s->second_timer2);
+    kill_timer(&s->pie_timer);
 }
index 8528d23b3284a3d230e0db5b4f3491ae226e064d..9eb54caaa6700fd211d85137d98e11f4c178fca8 100644 (file)
@@ -723,7 +723,10 @@ static void svm_freeze_time(struct vcpu *v)
             && !v->arch.hvm_vcpu.guest_time ) {
         v->arch.hvm_vcpu.guest_time = hvm_get_guest_time(v);
         if ( test_bit(_VCPUF_blocked, &v->vcpu_flags) )
+        {
             stop_timer(&pt->timer);
+            rtc_freeze(v);
+        }
     }
 }
 
@@ -850,24 +853,6 @@ int start_svm(void)
 }
 
 
-static void svm_migrate_timers(struct vcpu *v)
-{
-    struct periodic_time *pt = 
-        &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
-    struct RTCState *vrtc = &v->domain->arch.hvm_domain.pl_time.vrtc;
-    struct PMTState *vpmt = &v->domain->arch.hvm_domain.pl_time.vpmt;
-
-    if ( pt->enabled )
-    {
-        migrate_timer(&pt->timer, v->processor);
-    }
-    migrate_timer(&vcpu_vlapic(v)->vlapic_timer, v->processor);
-    migrate_timer(&vrtc->second_timer, v->processor);
-    migrate_timer(&vrtc->second_timer2, v->processor);
-    migrate_timer(&vpmt->timer, v->processor);
-}
-
-
 void arch_svm_do_resume(struct vcpu *v) 
 {
     /* pinning VCPU to a different core? */
@@ -880,7 +865,7 @@ void arch_svm_do_resume(struct vcpu *v)
             printk("VCPU core pinned: %d to %d\n", 
                    v->arch.hvm_svm.launch_core, smp_processor_id() );
         v->arch.hvm_svm.launch_core = smp_processor_id();
-        svm_migrate_timers( v );
+        hvm_migrate_timers( v );
         hvm_do_resume( v );
         reset_stack_and_jump( svm_asm_do_resume );
     }
index c32ece46e3b760a838bc7e2c2c08f4fcd61fbb45..2ac4262bfe5e0b2296c9f8d21f5d30a69aacb0e1 100644 (file)
@@ -471,8 +471,8 @@ void vioapic_set_irq(struct domain *d, int irq, int level)
     struct vioapic *vioapic = domain_vioapic(d);
     uint32_t bit;
 
-    HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_set_irq "
-                "irq %x level %x\n", irq, level);
+    HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_set_irq irq %x level %x", 
+                irq, level);
 
     if ( (irq < 0) || (irq >= VIOAPIC_NUM_PINS) )
         return;
index 45878d1c032787b9cf56ed0b6098e01a511646ee..cee05bc552d317e051d91d4defb50fb254fa9531 100644 (file)
@@ -486,7 +486,7 @@ void arch_vmx_do_resume(struct vcpu *v)
     {
         vmx_clear_vmcs(v);
         vmx_load_vmcs(v);
-        vmx_migrate_timers(v);
+        hvm_migrate_timers(v);
         vmx_set_host_env(v);
     }
 
index e7d3f8a9427e0508747cd115ec48a8c09c42ecb3..8382c6e148b6130dbecaddcba32d337ff6cacf1b 100644 (file)
@@ -376,14 +376,18 @@ static inline void vmx_restore_dr(struct vcpu *v)
 
 static void vmx_freeze_time(struct vcpu *v)
 {
-    struct periodic_time *pt=&v->domain->arch.hvm_domain.pl_time.periodic_tm;
+    struct hvm_domain *plat = &v->domain->arch.hvm_domain;
+    struct periodic_time *pt = &plat->pl_time.periodic_tm;
 
     if ( pt->enabled && pt->first_injected
             && (v->vcpu_id == pt->bind_vcpu)
             && !v->arch.hvm_vcpu.guest_time ) {
         v->arch.hvm_vcpu.guest_time = hvm_get_guest_time(v);
         if ( !test_bit(_VCPUF_blocked, &v->vcpu_flags) )
+        {
             stop_timer(&pt->timer);
+            rtc_freeze(v);
+        }
     }
 }
 
@@ -409,22 +413,6 @@ static void stop_vmx(void)
     clear_in_cr4(X86_CR4_VMXE);
 }
 
-void vmx_migrate_timers(struct vcpu *v)
-{
-    struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
-    struct RTCState *vrtc = &v->domain->arch.hvm_domain.pl_time.vrtc;
-    struct PMTState *vpmt = &v->domain->arch.hvm_domain.pl_time.vpmt;
-
-    if ( pt->enabled )
-    {
-        migrate_timer(&pt->timer, v->processor);
-    }
-    migrate_timer(&vcpu_vlapic(v)->vlapic_timer, v->processor);
-    migrate_timer(&vrtc->second_timer, v->processor);
-    migrate_timer(&vrtc->second_timer2, v->processor);
-    migrate_timer(&vpmt->timer, v->processor);
-}
-
 static void vmx_store_cpu_guest_regs(
     struct vcpu *v, struct cpu_user_regs *regs, unsigned long *crs)
 {
index 6576e20839bef3288a6d253d2603d8864388d116..69e6d1c9605587666120956fea2fa17d49680281 100644 (file)
@@ -161,6 +161,7 @@ hvm_get_guest_ctrl_reg(struct vcpu *v, unsigned int num)
 
 void hvm_stts(struct vcpu *v);
 void hvm_set_guest_time(struct vcpu *v, u64 gtime);
+void hvm_migrate_timers(struct vcpu *v);
 void hvm_do_resume(struct vcpu *v);
 
 static inline void
index eca3b683ed318e439a79f95d2c19a1f07f133dc5..881b2de38a0d3538a33a761552125955947fe3bf 100644 (file)
@@ -29,7 +29,6 @@
 extern void vmx_asm_vmexit_handler(struct cpu_user_regs);
 extern void vmx_asm_do_vmentry(void);
 extern void vmx_intr_assist(void);
-extern void vmx_migrate_timers(struct vcpu *v);
 extern void arch_vmx_do_resume(struct vcpu *);
 extern void set_guest_time(struct vcpu *v, u64 gtime);
 
index b4492e1d5945541ef62e2d326604953367c269a1..8e0e600194177d5642b4305220b363df46384518 100644 (file)
@@ -67,8 +67,10 @@ typedef struct RTCState {
     int64_t next_second_time;
     struct timer second_timer;
     struct timer second_timer2;
+    struct timer pie_timer;
+    int period;
+    s_time_t next_pie;
     struct vcpu      *vcpu;
-    struct periodic_time *pt;
 } RTCState;
 
 #define FREQUENCE_PMTIMER  3579545
@@ -143,9 +145,11 @@ extern void destroy_periodic_time(struct periodic_time *pt);
 void pit_init(struct vcpu *v, unsigned long cpu_khz);
 void rtc_init(struct vcpu *v, int base, int irq);
 void rtc_deinit(struct domain *d);
+void rtc_freeze(struct vcpu *v);
+void rtc_thaw(struct vcpu *v);
+void rtc_migrate_timers(struct vcpu *v);
 void pmtimer_init(struct vcpu *v, int base);
 void pmtimer_deinit(struct domain *d);
-int is_rtc_periodic_irq(void *opaque);
 void pt_timer_fn(void *data);
 void pit_time_fired(struct vcpu *v, void *priv);